﻿function Get-DocumentedBaseObject
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [PSObject] 
        $SourceObject,

        [Parameter(Mandatory=$false,ValueFromPipeline=$false)]
        [PSObject] 
        $ServerDefinition

    )
    Process
    {
        #Get Standard properties
        if (-not $ServerDefinition) {$server = Get-Server -CurrentObject $sourceObject}
        else {$server = $ServerDefinition}
        $DeploymentTarget = Get-DeploymentTarget -CurrentObject $server
        $ensure = Get-EnsureStatus -CurrentObject $sourceObject
        $uniqueKey = Resolve-FullQualifiedUniqueKey -CurrentObject $sourceObject

        #Create the new object model
        $targetObject = New-Object PsObject
        $targetObject | Add-Member -MemberType NoteProperty -Name UniqueKey -Value $uniqueKey
        $targetObject | Add-Member -MemberType NoteProperty -Name Server -Value $server.HostName
        $targetObject | Add-Member -MemberType NoteProperty -Name Environment -Value $DeploymentTarget.Environment
        $targetObject | Add-Member -MemberType NoteProperty -Name Level -Value $sourceObject.Level
        
        Write-Output $targetObject
    }
}



function new-XmlDocumentationDataSource
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String] 
        $Path
    )
    Process
    {
        if(!(Test-Path $Path))
        {
            $doc = New-Object System.Xml.XmlDocument

            $description = $doc.CreateXmlDeclaration("1.0",$null,$null)
            $doc.AppendChild($description)

            $root = $doc.CreateElement("documentation")
            $doc.AppendChild($root)
            $doc.Save($Path)
        }
    }
}

Function Get-XmlDocumentationDataSource
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String] 
        $Path
    )
     Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Get-XmlDocumentationDataSource in  $Path"
    }
    End
    {
        write-verbose "End Get-XmlDocumentationDataSource in  $Path"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process
    {
        
        #new xml log file if it doesnt exist
        new-XmlDocumentationDataSource -Path $Path | Out-Null

        #read the file log
        $doc = New-Object System.Xml.XmlDocument
        $doc.Load($Path)

        Write-Output $doc
   
    }
}


Function Set-XmlDocumentationNode
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Xml.XmlDocument] 
        $Document,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Xml.XmlElement] 
        $ParentNode,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String] 
        $ElementName,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Object] 
        $InputObject

        )
    Process
    {
        
        $newNode = $Document.CreateElement($ElementName)
        $ParentNode.AppendChild($newNode)

        $InputObject | Get-Member -MemberType NoteProperty, Property, CodeProperty | ForEach-Object{
            if ($_.Definition.StartsWith("System.Object[]") -or $_.Definition.StartsWith("Deserialized.System.Object[]") -or $_.Definition.StartsWith("System.Security"))
            {
                Write-verbose "Add collectionas child nodes for property $($_.Name) with type $($_.Definition)"

                $collectionElements = $InputObject.$($_.Name)
                foreach($element in $collectionElements)
                {
                    
                    try
                    {
                        $elementType=$element.GetType().Name    
                    }
                    catch
                    {}
                
                
                    if ($elementType -eq "String")
                    {
                        $attributeValue = $element

                        $newNodeLineResult = $Document.CreateElement("detailedResultLine")
                        $newNode.AppendChild($newNodeLineResult)
                        $newNodeLineResult.InnerText = $attributeValue
                        
                        write-verbose "Add attribute $($_.Name) with type String and value of $attributeValue"
                        if (($attributeValue -eq $null) -or ([System.String]::IsNullorEmpty($attributeValue)))
                        {
                            $attributeValue = ""
                        }
						$attributeValue = $attributeValue -replace "\s-password\s\S+"
                        $newNode.SetAttribute($($_.Name),$attributeValue)
                    }
                    else
                    {
                        Set-XmlNode -Document $Document -ParentNode $newNode -ElementName $_.Name -InputObject $element | Out-Null
                    }    
                }
            }
            else
            {
                if ($_.Definition.StartsWith("System.Object") -or $_.Definition.StartsWith("Deserialized.") -or $_.Definition.StartsWith("System.Security"))
                {
                    Write-verbose "Expand object for property $($_.Name) with type $($_.Definition)"
                    Set-XmlNode -Document $Document -ParentNode $newNode -ElementName $_.Name -InputObject $InputObject.$($_.Name) | Out-Null
                }
                else
                {
                    $attributeValue = $InputObject.$($_.Name)
                    write-verbose "Add attribute $($_.Name) with type $($_.Definition) and value of $attributeValue"
                    if (($attributeValue -eq $null) -or ([System.String]::IsNullorEmpty($attributeValue)))
                    {
                        $attributeValue = ""
                    }

					$attributeValue = $attributeValue -replace "\s-password\s\S+"
                    $newNode.SetAttribute($($_.Name),$attributeValue)
                }
            }
            

        }
        Write-Output $newNode

    }


}



Function Set-XmlDocumentationRootNode
{
   Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Xml.XmlDocument] 
        $Document,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Xml.XmlElement] 
        $RootDocumentationNode,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String] 
        $DocumentationType
        )
     Process
    {
        $documentationNode = $RootDocumentationNode.SelectSingleNode("//$DocumentationType")
        if ($documentationNode -eq $null)
        {
            $newNode = $Document.CreateElement($DocumentationType)
            $RootDocumentationNode.AppendChild($newNode)
        }

        $documentationNode = $RootDocumentationNode.SelectSingleNode("//$DocumentationType")
        Write-Output $documentationNode

    }
}

function Add-DocumentationElement
{
    Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String] 
        $Path,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String] 
        $DocumentationType,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String] 
        $UniqueKey,

        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [PsObject] 
        $DocumentationObject
        )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Add-DocumentationElement in  $Path"
    }
    End
    {
        write-verbose "End Add-DocumentationElement in  $Path"
        $watch.Stop();
        Write-Verbose "Function Duration : $($watch.Elapsed)"
    }
    Process
    {
        $document = Get-XmlDocumentationDataSource -Path $Path
        $rootDocumentationType = Set-XmlDocumentationRootNode -Document $document -RootDocumentationNode $document.DocumentElement -DocumentationType $DocumentationType

        $documentationNode = $rootDocumentationType.SelectSingleNode("//$DocumentationType[@UniqueKey='$UniqueKey']")

        if ($documentationNode -ne $null)
        {
            [System.Xml.XmlElement]$rootDocumentationType.RemoveChild($documentationNode)
        }


        Set-XmlDocumentationNode -Document $document -ParentNode $rootDocumentationType -ElementName $DocumentationType -InputObject $DocumentationObject  | Out-Null

        $document.Save($Path)
    }
}


